package com.cw.util;

import com.cw.annotation.CopyBeanProperty;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 对象拷贝(工具类?)
 * @author ly
 * @since 2021-11-9
 */
public class BeanCopyUtil {

    /**
     * 反射拷贝bean对象
     * @param source 拷贝的数据源
     * @param target 需要拷贝的集合
     * @author ly
     * @date 2021/8/12 14:29
     */
    public static void copyProperty(Object source, Object target) {
        //搞到对象的字节码文件
        Class<?> aClass = target.getClass();

        Class<?> sourceClass = source.getClass();
        //获取对象中所有声明的字段public private protected都获取
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String columnName = declaredField.getName();
            //是否需要重更名的注解
            boolean annotationPresent = declaredField.isAnnotationPresent(CopyBeanProperty.class);
            if (!annotationPresent) {
                //赋值
                boolean assignment = assignment(sourceClass, columnName, aClass, source, target);
                if (!assignment) { continue; }
                continue;
            }

            CopyBeanProperty property = declaredField.getAnnotation(CopyBeanProperty.class);
            String name = property.value();
            assignment(sourceClass, name, aClass, source, target, columnName);
        }
    }


    /**
     * 给target对象赋值
     * @param sourceClass 赋值参考对象的字节码类
     * @param columnName 赋值参考对象中的变量名
     * @param targetClass 被赋值的对象的字节码
     * @param source 赋值参考对象
     * @param target 被赋值的对象
     * @param targetMethodName 被赋值对象中的变量名
     * @author ly
     * @date 2021/11/9 15:44
     * @return boolean (false为未赋值,true者相反)
     */
    private static boolean assignment(Class<?> sourceClass, String columnName, Class<?> targetClass, Object source, Object target, String targetMethodName) {
        try {
            String methodName = upperFirstLetter(columnName);
            //直接在拷贝的数据源,执行get某参数的方法
            //如果没有这个参数就进入下一个参数循环吧
            Method declaredMethod = sourceClass.getDeclaredMethod("get" + methodName);
            //获取到它的参数
            Object entityValue = declaredMethod.invoke(source);
            if (entityValue == null) {
                return false;
            }

            //需要赋值参数的变量名
            if (targetMethodName != null ) { methodName = upperFirstLetter(targetMethodName); }

            //判断类型是否一致,不一致不赋值
            assert targetMethodName != null;
            Field targetField = targetClass.getDeclaredField(targetMethodName);
            Field sourceField = sourceClass.getDeclaredField(columnName);
            Class<?> targetType = targetField.getType();
            Class<?> sourceType = sourceField.getType();
            if (targetType != sourceType) { return false; }

            //赋值给target
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(methodName, targetClass);
            Method writeMethod = propertyDescriptor.getWriteMethod();
            writeMethod.invoke(target, entityValue);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | IntrospectionException | NoSuchFieldException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    private static boolean assignment(Class<?> sourceClass, String columnName, Class<?> targetClass, Object source, Object target) {
        return assignment(sourceClass, columnName, targetClass, source, target, columnName);
    }

    /**
     * 首个字母大写
     * @param name 需要处理的字符串
     * @author ly
     * @date 2021/8/12 15:28
     * @return java.lang.String
     */
    private static String upperFirstLetter(String name) {
        //因为lombok生成出来的getter setter方法,是首字母大写变量名
        //为了我们严谨的反射机制小朋友能找到它,这里给他首字母大写
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }
}
